// pages/meeting/meeting.js
const app = getApp()
const Utils = require('../../utils/util.js')
const AgoraMiniappSDK = require("../../lib/Agora_Miniapp_SDK_for_WeChat.js");
const max_user = 10;
const Layouter = require("../../utils/layout.js");
const APPID = require("../../utils/config.js").APPID;
const Uploader = require("../../utils/uploader.js")
const LogUploader = Uploader.LogUploader;
const LogUploaderTask = Uploader.LogUploaderTask;

Page({
  data: {
    media: [],
    muted: false,
    beauty: 0,
    totalUser: 1,
    debug: false
  },

  onLoad: function(options) {
    this.channel = options.channel;
    this.role = options.role || "broadcaster";
    // this.uid = Utils.getUid();
    this.uid = options.uid
    this.client = null;
    this.layouter = null;
    this.leaving = false;
    wx.setNavigationBarTitle({
      title: `${this.channel}(${this.uid})`
    });
    wx.setKeepScreenOn({
      keepScreenOn: true
    });
  },

  onReady: function() {
    const channel = this.channel;
    const uid = this.uid;
    // 每一个小时上传日志
    // this.logTimer = setInterval(() => {
    //   this.uploadLogs();
    // }, 60 * 60 * 1000);
    this.initLayouter();
    this.initAgoraChannel(uid, channel).then(url => {
      const ts = new Date().getTime();
      if (this.isBroadcaster()) {
        this.addMedia(0, uid, url, { key: ts });
      }
    }).catch(e => {
      wx.showToast({
        title: `客户端初始化失败`,
        icon: 'none',
        duration: 5000
      });
    });
  },

  onShareAppMessage() {
  },

  syncLayout(media) {
    let sizes = this.layouter.getSize(media.length);
    for (let i = 0; i < sizes.length; i++) {
      let size = sizes[i];
      let item = media[i];
      if (item.holding) {
        continue;
      }
      item.left = parseFloat(size.x).toFixed(2);
      item.top = parseFloat(size.y).toFixed(2);
      item.width = parseFloat(size.width).toFixed(2);
      item.height = parseFloat(size.height).toFixed(2);
    }
    return media;
  },

  hasMedia(mediaType, uid) {
    let media = this.data.media || [];
    return media.filter(item => {
      return item.type === mediaType && `${item.uid}` === `${uid}`
    }).length > 0
  },

  addMedia(mediaType, uid, url, options) {
    let media = this.data.media || [];
    if (mediaType === 0) {
      media.splice(0, 0, {
        key: options.key,
        type: mediaType,
        uid: uid,
        holding: false,
        url: url,
        left: 0,
        top: 0,
        width: 0,
        height: 0
      });
    } else {
      media.push({
        key: options.key,
        rotation: options.rotation,
        type: mediaType,
        uid: uid,
        holding: false,
        url: url,
        left: 0,
        top: 0,
        width: 0,
        height: 0
      });
    }

    media = this.syncLayout(media);
    return this.refreshMedia(media);
  },

  removeMedia: function(uid) {
    let media = this.data.media || [];
    media = media.filter(item => item.uid !== uid);
    media = this.syncLayout(media);
    this.refreshMedia(media);
    // if (media.length !== this.data.media.length) {
    //   media = this.syncLayout(media);
    //   this.refreshMedia(media);
    // } else {
    //   return Promise.resolve();
    // }
  },

  updateMedia: function(uid, options) {
    let media = this.data.media || [];
    let changed = false;
    for (let i = 0; i < media.length; i++) {
      let item = media[i];
      if (item.uid === uid) {
        media[i] = Object.assign(item, options);
        changed = true;
        break;
      }
    }

    if (changed) {
      return this.refreshMedia(media);
    } else {
      return Promise.resolve();
    }
  },

  refreshMedia: function(media) {
    return new Promise((resolve) => {
      for (let i = 0; i < media.length; i++) {
        if (i < max_user) {
          //show
          media[i].holding = false;
        } else {
          //hide 
          media[i].holding = true;
        }
      }

      if (media.length > max_user) {
        wx.showToast({
          title: '由于房内人数超过10人，部分视频未被加载显示',
        });
      }
      this.setData({
        media: media
      }, () => {
        resolve();
      });
    });
  },

  onShow: function() {
    let media = this.data.media || [];
    media.forEach(item => {
      if (item.type === 0) {
        return;
      }
      let player = this.getPlayerComponent(item.uid);
      player && player.start()
      // if (!player) {
      //   console.log(`player ${item.uid} component no longer exists`);
      // } else {
      //   player.start();
      // }
    });
  },
  onHide: function() {},

  onError: function(e) {
    Utils.log(`error: ${JSON.stringify(e)}`);
  },

  onUnload: function() {
    // Utils.log(`onUnload`);
    // clearInterval(this.logTimer);
    // clearTimeout(this.reconnectTimer);
    // this.logTimer = null;
    // this.reconnectTimer = null;

    // unlock index page join button
    let pages = getCurrentPages();
    if (pages.length > 1) {
      //unlock join
      let indexPage = pages[0];
      indexPage.unlockJoin();
    }

    // unpublish sdk and leave channel
    if (this.isBroadcaster()) {
      try {
        this.client && this.client.unpublish();
      } catch (e) {
        Utils.log(`unpublish failed ${e}`);
      }
    }
    this.client && this.client.leave();
  },

  onLeave: function() {
    if (!this.leaving) {
      this.leaving = true;
      this.navigateBack();
    }
  },


  navigateBack: function() {
    if (getCurrentPages().length > 1) {
      //have pages on stack
      wx.navigateBack({});
    } else {
      //no page on stack, usually means start from shared links
      wx.redirectTo({
        url: '../index/index',
      });
    }
  },

  onPusherFailed: function() {
    wx.showModal({
      title: '发生错误',
      content: '推流发生错误，请重新进入房间重试',
      showCancel: false,
      success: () => {
        this.navigateBack();
      }
    })
  },

  onMute: function() {
    if (!this.data.muted) {
      this.client.muteLocal('audio', () => {
        console.log('muteLocal success')
      }, err => {
        console.log(err)
      });
    } else {
      this.client.unmuteLocal('audio', () => {
        console.log('unmuteLocal success')
      }, err => {
        console.log(err)
      });
    }
    this.setData({
      muted: !this.data.muted
    })
  },

  onSwitchCamera: function() {
    const agoraPusher = this.getPusherComponent();
    agoraPusher && agoraPusher.switchCamera();
  },

  onMakeup: function() {
    let beauty = this.data.beauty == 5 ? 0 : 5;
    this.setData({
      beauty: beauty
    })
  },

  uploadLogs: function() {
    let logs = Utils.getLogs();
    Utils.clearLogs();

    let totalLogs = logs.length;
    let tasks = [];
    let part = 0;
    let ts = new Date().getTime();
    // 1w logs per task slice
    const sliceSize = 500;
    do {
      let content = logs.splice(0, sliceSize);
      tasks.push(new LogUploaderTask(content, this.channel, part++, ts, this.uid));
    } while (logs.length > sliceSize)
    wx.showLoading({
      title: '0%',
      mask: true
    })
    LogUploader.off("progress").on("progress", e => {
      let remain = e.remain;
      let total = e.total;
      Utils.log(`log upload progress ${total - remain}/${total}`);
      if (remain === 0) {
        wx.hideLoading();
        wx.showToast({
          title: `上传成功`,
        });
      } else {
        wx.showLoading({
          mask: true,
          title: `${((total - remain) / total * 100).toFixed(2)}%`,
        })
      }
    });
    LogUploader.on("error"), e => {
      wx.hideLoading();
      wx.showToast({
        title: `上传失败: ${e}`,
      });
    }
    LogUploader.scheduleTasks(tasks);
  },

  /**
   * 上传日志回调
   */
  onSubmitLog: function() {
    let page = this;
    let mediaAction = this.isBroadcaster() ? "下麦" : "上麦"
    wx.showActionSheet({
      itemList: [mediaAction, "上传日志"],
      success: res => {
        let tapIndex = res.tapIndex;
        if (tapIndex == 0) {
          if (this.isBroadcaster()) {
            this.becomeAudience().then(() => {
              this.removeMedia(this.uid);
            }).catch(e => {
              Utils.log(`switch to audience failed ${e.stack}`);
            })
          } else {
            let ts = new Date().getTime();
            this.becomeBroadcaster().then(url => {
              this.addMedia(0, this.uid, url, {
                key: ts
              });
            }).catch(e => {
              Utils.log(`switch to broadcaster failed ${e.stack}`);
            })
          }
        } else if (tapIndex === 1) {
          this.setData({
            debug: !this.data.debug
          })
          wx.showModal({
            title: '遇到使用问题?',
            content: '点击确定可以上传日志，帮助我们了解您在使用过程中的问题',
            success: function(res) {
              if (res.confirm) {
                console.log('用户点击确定')
                page.uploadLogs();
              } else if (res.cancel) {
                console.log('用户点击取消')
              }
            }
          })
        }
      }
    })
  },

  initLayouter: function() {
    const systemInfo = app.globalData.systemInfo;
    this.layouter = new Layouter(systemInfo.windowWidth, systemInfo.windowHeight - 64);
  },

  initAgoraChannel: function(uid, channel) {
    return new Promise((resolve, reject) => {
      const client = new AgoraMiniappSDK.Client()
      this.subscribeEvents(client);
      // AgoraMiniappSDK.LOG.onLog = (text) => {
      //   Utils.log(text);
      // };
      // AgoraMiniappSDK.LOG.setLogLevel(-1);
      this.client = client;
      client.setRole(this.role);
      client.init(APPID, () => {
        client.join(undefined, channel, uid, () => {
          if (this.isBroadcaster()) {
            client.publish(url => {
              resolve(url);
            }, e => {
              reject(e)
            });
          } else {
            resolve();
          }
        }, e => {
          reject(e)
        })
      }, e => {
        reject(e);
      });
    });
  },

  reinitAgoraChannel: function(uid, channel) {
    return new Promise((resolve, reject) => {
      let client = {}
      client = new AgoraMiniappSDK.Client()
      // if (this.testEnv) {
      //   client = new AgoraMiniappSDK.Client({
      //     servers: ["wss://miniapp.agoraio.cn/120-131-14-112/api"]
      //   });
      // } else {
      //   client = new AgoraMiniappSDK.Client()
      // }
      //subscribe stream events
      this.subscribeEvents(client);
      AgoraMiniappSDK.LOG.onLog = (text) => {
        // callback to expose sdk logs
        Utils.log(text);
      };
      AgoraMiniappSDK.LOG.setLogLevel(-1);
      let uids = this.data.media.map(item => {
        return item.uid;
      });
      this.client = client;
      client.setRole(this.role);
      client.init(APPID, () => {
        client.rejoin(undefined, channel, uid, uids, () => {
          if (this.isBroadcaster()) {
            client.publish(url => {
              resolve(url);
            }, e => {
              reject(e)
            });
          } else {
            resolve();
          }
        }, e => {
          reject(e)
        })
      }, e => {
        reject(e);
      });
    });
  },

  /**
   * return player component via uid
   */
  getPlayerComponent: function(uid) {
    const agoraPlayer = this.selectComponent(`#rtc-player-${uid}`);
    return agoraPlayer;
  },

  /**
   * return pusher component
   */
  getPusherComponent: function() {
    const agorapusher = this.selectComponent(`#rtc-pusher`);
    return agorapusher;
  },

  becomeBroadcaster: function() {
    return new Promise((resolve, reject) => {
      if (!this.client) {
        return reject(new Error("no client available"))
      }
      let client = this.client
      this.role = "broadcaster"
      client.setRole(this.role)
      Utils.log(`client switching role to ${this.role}`);
      client.publish(url => {
        Utils.log(`client publish success`);
        resolve(url);
      }, e => {
        Utils.log(`client publish failed: ${e.code} ${e.reason}`);
        reject(e)
      });
    })
  },

  becomeAudience: function() {
    return new Promise((resolve, reject) => {
      if (!this.client) {
        return reject(new Error("no client available"))
      }

      let client = this.client
      client.unpublish(() => {
        Utils.log(`client unpublish success`);
        this.role = "audience"
        Utils.log(`client switching role to ${this.role}`);
        client.setRole(this.role)
        resolve();
      }, e => {
        Utils.log(`client unpublish failed: ${e.code} ${e.reason}`);
        reject(e)
      });
    })
  },

  /**
   * reconnect when bad things happens...
   */
  reconnect: function() {
    wx.showToast({
      title: `尝试恢复连接...`,
      icon: 'none',
      duration: 5000
    });
    this.client && this.client.destroy();
    this.reconnectTimer = setTimeout(() => {
      let uid = this.uid;
      let channel = this.channel;
      this.reinitAgoraChannel(uid, channel).then(url => {
        let ts = new Date().getTime();
        if (this.isBroadcaster()) {
          if (this.hasMedia(0, this.uid)) {
            this.updateMedia(this.uid, {
              url: url,
              key: ts,
            });
          } else {
            this.addMedia(0, this.uid, url, {
              key: ts
            });
          }
        }
      }).catch(e => {
        return this.reconnect();
      });
    }, 1 * 1000);
  },

  /**
   * 如果
   */
  isBroadcaster: function() {
    return this.role === "broadcaster";
  },

  /**
   * 注册stream事件
   */
  subscribeEvents: function(client) {
    // 通知应用程序远端视频已旋转
    client.on("video-rotation", (e) => {
      setTimeout(() => {
        const player = this.getPlayerComponent(e.uid);
        player && player.rotate(e.rotation);
      }, 1000);
    });
    // 通知应用程序已添加远端音视频流。
    client.on("stream-added", e => {
      let uid = e.uid;
      const ts = new Date().getTime();
      client.subscribe(uid, (url, rotation) => {
        let media = this.data.media || [];
        let matchItem = null;
        for (let i = 0; i < media.length; i++) {
          let item = this.data.media[i];
          if (item.uid === uid) {
            matchItem = item;
            break;  
          }
        }

        if (!matchItem) {
          //if not existing, add new media
          this.addMedia(1, uid, url, {
            key: ts,
            rotation: rotation
          })
        } else {
          this.updateMedia(matchItem.uid, {
            url: url,
            key: ts,
          });
        }
      }, e => {
        Utils.log(`stream subscribed failed ${e} ${e.code} ${e.reason}`);
      });
    });
    // 通知应用程序已删除远端音视频流。
    client.on("stream-removed", e => {
      let uid = e.uid;
      Utils.log(`stream ${uid} removed`);
      this.removeMedia(uid);
    });
    // 通知应用程序发生错误。
    client.on("error", err => {
      let errObj = err || {};
      let code = errObj.code || 0;
      let reason = errObj.reason || "";
      Utils.log(`error: ${code}, reason: ${reason}`);
      let ts = new Date().getTime();
      if (code === 501 || code === 904) {
        this.reconnect();
      }
    });
    // 通知应用程序已更新 Url 地址。
    client.on('update-url', e => {
      let uid = e.uid;
      let url = e.url;
      let ts = new Date().getTime();
      if (`${uid}` === `${this.uid}`) {
      } else {
        this.updateMedia(uid, {
          url: url,
          key: ts,
        });
      }
    });
  }
})